package com.jse;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
 
/**
 * 
 * @Description: 带有效期map
 * @param <K>
 * @param <V>
 */
public class Cache<K, V> extends LinkedHashMap<K, V> {
 
	private static final long serialVersionUID = Long.MAX_VALUE/10;
 
	private final int maxSize;
	/**
	 * 过期时间设置为2小时，可以修改
	 */
	private long EXPIRY = serialVersionUID;
 
	private HashMap<K, Long> expiryMap = new HashMap<>();
	private Function<Object,V> fun;

	private BiConsumer<K,V> consumer;
 
	public Cache() {
		this(1024);
	}
 
	public Cache(int maxSize) {
		this(maxSize,serialVersionUID);
	}
 
	public Cache(int maxSize, long ms) {
		this(maxSize,ms,16, 0.75f, false);
		
	}
	public Cache(int maxSize,long ms,int initialCapacity, float loadFactor, boolean accessOrder){
		super(initialCapacity,loadFactor,accessOrder);
        this.maxSize = maxSize;
        this.EXPIRY = ms;
    }
	
	@Override
	protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {//lru
        return this.size() > this.maxSize;
    }
 
	public V put(K key, V value) {
		expiryMap.put(key, System.currentTimeMillis() + EXPIRY);
		return super.put(key, value);
	}
 
	public boolean containsKey(Object key) {
		return !checkExpiry(key, true) && super.containsKey(key);
	}
 
	/**
	 * @param key
	 * @param value
	 * @param expiryTime
	 *            键值对有效期 毫秒
	 * @return
	 */
	public V put(K key, V value, long expiryTime) {
		return put(key, value, expiryTime,null);
	}
	
	public V put(K key, V value, long expiryTime,Function<Object,V> fun) {
		expiryMap.put(key, System.currentTimeMillis() + expiryTime);
		this.fun=fun;
		return super.put(key, value);
	}
 
	public int size() {
		return entrySet().size();
	}
 
	public boolean isEmpty() {
		return entrySet().size() == 0;
	}
 
	public boolean containsValue(Object value) {
		if (value == null)
			return Boolean.FALSE;
		Set<java.util.Map.Entry<K, V>> set = super.entrySet();
		Iterator<java.util.Map.Entry<K, V>> iterator = set.iterator();
		while (iterator.hasNext()) {
			java.util.Map.Entry<K, V> entry = iterator.next();
			if (value.equals(entry.getValue())) {
				if (checkExpiry(entry.getKey(), false)) {
					iterator.remove();
					return Boolean.FALSE;
				} else
					return Boolean.TRUE;
			}
		}
		return Boolean.FALSE;
	}
 
	public Collection<V> values() {
 
		Collection<V> values = super.values();
 
		if (values == null || values.size() < 1)
			return values;
 
		Iterator<V> iterator = values.iterator();
 
		while (iterator.hasNext()) {
			V next = iterator.next();
			if (!containsValue(next))
				iterator.remove();
		}
		return values;
	}
 
	public V get(Object key) {
		if (key == null) {
			return null;
		}
		if (checkExpiry(key, true)) {
			if(fun!=null) {
				return fun.apply(key);
			}
			return null;
		}
		return super.get(key);
	}
	public <T>T get(Object key,T d){V v=get(key);return v==null?d:(T)v;}
 
	/**
	 * 
	 * @Description: 是否过期
	 * @param key
	 * @return null:不存在或key为null -1:过期 存在且没过期返回value 因为过期的不是实时删除，所以稍微有点作用
	 */
	public Object isInvalid(Object key) {
		if (key == null)
			return null;
		if (!expiryMap.containsKey(key)) {
			return null;
		}
		long expiryTime = expiryMap.get(key);
 
		boolean flag = System.currentTimeMillis() > expiryTime;
 
		if (flag) {
			super.remove(key);
			expiryMap.remove(key);
			return -1;
		}
		return super.get(key);
	}
 
	public void putAll(Map<? extends K, ? extends V> m) {
		for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
			expiryMap.put(e.getKey(), System.currentTimeMillis() + EXPIRY);
		super.putAll(m);
	}
 
	public Set<Map.Entry<K, V>> entrySet() {
		Set<java.util.Map.Entry<K, V>> set = super.entrySet();
		Iterator<java.util.Map.Entry<K, V>> iterator = set.iterator();
		while (iterator.hasNext()) {
			java.util.Map.Entry<K, V> entry = iterator.next();
			if (checkExpiry(entry.getKey(), false))
				iterator.remove();
		}
 
		return set;
	}
 
	/**
	 * 
	 * @Description: 是否过期
	 * @param expiryTime
	 *            true 过期
	 * @param isRemoveSuper
	 *            true super删除
	 * @return
	 */
	private boolean checkExpiry(Object key, boolean isRemoveSuper) {
 
		if (!expiryMap.containsKey(key)) {
			return Boolean.FALSE;
		}
		long expiryTime = expiryMap.get(key);
 
		boolean flag = System.currentTimeMillis() > expiryTime;
 
		if (flag) {
			if (isRemoveSuper)
				super.remove(key);
			expiryMap.remove(key);
			if(consumer!=null)
				consumer.accept((K)key,get(key));
		}
		return flag;
	}
	
	public V get(K k,Supplier<V> s) {
		V v=get(k);
		return v!=null?v:s.get();
	}

	public Cache<K,V> addListener(BiConsumer<K, V> consumer) {
		this.consumer=consumer;
		return this;
	}
	

	 
	public static void main(String[] args) throws InterruptedException {
		Cache<String, String> map = new Cache<>(2);
		map.put("test", "ankang1");
		map.put("test1", "ankang2");
		map.put("test2", "ankang3", 100,(k)->{
			return "ankang"+k;
		});
		Thread.sleep(1000);
		for (String k : map.keySet()) {
			System.out.println(k+":"+map.get(k));
		}
	}
}